﻿#region Copyright
/// <copyright>
/// Copyright (c) 2008 Ilia Shramko.
/// Released under the terms of the GNU General Public License.
/// </copyright>
#endregion

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace Osh.Media
{
	public class MediaLibraryNavigator
	{
		private IList<IMediaLibrary> _libraries;
		private IMediaLibrary _currentLibrary;
		private IMediaTree _currentTree;
		private MediaTreeNavigator _currentTreeNavigator;

		public MediaLibraryNavigator() : this(new List<IMediaLibrary>())
		{
		}

		public MediaLibraryNavigator(IList<IMediaLibrary> libraries) : this(libraries, true)
		{
		}

		private MediaLibraryNavigator(IList<IMediaLibrary> libraries, bool dontMove)
		{
			if(libraries == null)
				throw new ArgumentNullException("libraries");
			_libraries = libraries;
			if(!dontMove)
				MoveToFirstLibrary();
		}

		public IList<IMediaLibrary> Libraries
		{
			get { return _libraries; }
		}

		public bool MoveToFirstLibrary()
		{
			if(_libraries.Count == 0)
				return false;

			_currentLibrary = _libraries[0];
			this.CurrentTree = null;

			MoveToFirstLibraryTree();
			return true;
		}

		public bool MoveToLastLibrary()
		{
			if(_libraries.Count == 0)
				return false;

			_currentLibrary = _libraries[_libraries.Count - 1];
			this.CurrentTree = null;

			MoveToFirstLibraryTree();
			return true;
		}

		public bool MoveToNextLibrary()
		{
			if(_libraries.Count == 0)
				return false;

			int index = _currentLibrary == null ? 0 : Math.Max(_libraries.Count - 1, _libraries.IndexOf(_currentLibrary) + 1);
			IMediaLibrary nextLibrary = _libraries[index];
			if(_currentLibrary == nextLibrary)
				return false;

			_currentLibrary = nextLibrary;
			this.CurrentTree = null;

			MoveToFirstLibraryTree();
			return true;
		}

		public bool MoveToPreviousLibrary()
		{
			if(_libraries.Count == 0)
				return false;

			int index = _currentLibrary == null ? 0 : Math.Min(0, _libraries.IndexOf(_currentLibrary) - 1);
			IMediaLibrary previousLibrary = _libraries[index];
			if(_currentLibrary == previousLibrary)
				return false;

			_currentLibrary = previousLibrary;
			this.CurrentTree = null;

			MoveToFirstLibraryTree();
			return true;
		}

		public bool MoveToFirstLibraryTree()
		{
			if(_currentLibrary == null)
				return false;

			IList<IMediaTree> trees = _currentLibrary.MediaTrees;
			if(trees.Count == 0)
				return false;

			this.CurrentTree = trees[0];
			return true;
		}

		public bool MoveToLastLibraryTree()
		{
			if(_currentLibrary == null)
				return false;

			IList<IMediaTree> trees = _currentLibrary.MediaTrees;
			if(trees.Count == 0)
				return false;

			this.CurrentTree = trees[trees.Count - 1];
			return true;
		}

		public bool MoveToFirstTree()
		{
			if(_libraries.Count == 0)
				return false;

			for(int l = 0; l < _libraries.Count; l++)
			{
				IMediaLibrary library = _libraries[l];
				if(library.MediaTrees.Count == 0)
					continue;

				_currentLibrary = library;
				this.CurrentTree = library.MediaTrees[0];
				return true;
			}
			return false;
		}

		public bool MoveToLastTree()
		{
			if(_libraries.Count == 0)
				return false;

			for(int l = _libraries.Count - 1; l >= 0; l--)
			{
				IMediaLibrary library = _libraries[l];
				if(library.MediaTrees.Count == 0)
					continue;

				_currentLibrary = library;
				this.CurrentTree = library.MediaTrees[library.MediaTrees.Count - 1];
				return true;
			}
			return false;
		}

		public bool MoveToNextTree()
		{
			if(_libraries.Count == 0)
				return false;

			IMediaLibrary currentLibrary = _currentLibrary;
			IMediaTree currentTree = _currentTree;

			int currentLibraryIndex = currentLibrary == null ? -1 : _libraries.IndexOf(currentLibrary);
			int currentTreeIndex = -1;
			if(currentLibrary != null && currentTree != null)
				currentTreeIndex = currentLibrary.MediaTrees.IndexOf(currentTree);

			if(currentLibraryIndex == -1 || currentTreeIndex == -1)
				return MoveToFirstTree();

			for(int l = currentLibraryIndex; l < _libraries.Count; l++)
			{
				IMediaLibrary library = _libraries[l];
				if(currentTreeIndex == -1)
				{
					if(library.MediaTrees.Count > 0)
					{
						_currentLibrary = library;
						this.CurrentTree = library.MediaTrees[0];
						return true;
					}
				}
				else
				{
					if(currentTreeIndex + 1 < library.MediaTrees.Count)
					{
						_currentLibrary = library;
						this.CurrentTree = library.MediaTrees[currentTreeIndex + 1];
						return true;
					}
					currentTreeIndex = -1;
				}
			}
			return false;
		}

		public bool MoveToPreviousTree()
		{
			if(_libraries.Count == 0)
				return false;

			IMediaLibrary currentLibrary = _currentLibrary;
			IMediaTree currentTree = _currentTree;

			int currentLibraryIndex = currentLibrary == null ? -1 : _libraries.IndexOf(currentLibrary);
			int currentTreeIndex = -1;
			if(currentLibrary != null && currentTree != null)
				currentTreeIndex = currentLibrary.MediaTrees.IndexOf(currentTree);

			if(currentLibraryIndex == -1 || currentTreeIndex == -1)
				return MoveToFirstTree();

			for(int l = currentLibraryIndex; l >= 0; l--)
			{
				IMediaLibrary library = _libraries[l];
				if(currentTreeIndex == -1)
				{
					if(library.MediaTrees.Count > 0)
					{
						_currentLibrary = library;
						this.CurrentTree = library.MediaTrees[library.MediaTrees.Count - 1];
						return true;
					}
				}
				else
				{
					if(currentTreeIndex - 1 >= 0)
					{
						_currentLibrary = library;
						this.CurrentTree = library.MediaTrees[currentTreeIndex - 1];
						return true;
					}
					currentTreeIndex = -1;
				}
			}
			return false;
		}

		/// <summary>
		/// Moves to the first media tree with the passed defaul media player.
		/// If the current media tree has the same default media player position does not change.
		/// </summary>
		/// <param name="player"></param>
		/// <returns></returns>
		public bool MoveToTreeWithDefaultPlayer(IMediaPlayer player)
		{
			Type playerType = player != null ? player.GetType() : null;

			if(this.CurrentLibrary != null && this.CurrentLibrary.DefaultPlayer == playerType)
				return true;

			MediaLibraryNavigator navigator = this.Clone();
			if(!navigator.MoveToFirstTree())
				return false;

			do
			{
				if(navigator.CurrentLibrary.DefaultPlayer == playerType)
					return this.MoveTo(navigator);
			}
			while(navigator.MoveToNextTree());

			return false;
		}

		public bool MoveTo(MediaLibraryNavigator position)
		{
			if(position == null)
				throw new ArgumentNullException("position");
			
			if(position._currentLibrary != null)
			{
				if(!_libraries.Contains(position._currentLibrary))
					return false;
			}

			this._currentLibrary = position._currentLibrary;
			this._currentTree = position._currentTree;
			this._currentTreeNavigator = 
				position._currentTreeNavigator != null ? position._currentTreeNavigator.Clone() : null;
			return true;
		}

		[ComVisible(false)]
		public bool MoveToMedia(Uri mediaUri)
		{
			MediaLibraryNavigator navigator = this.Clone();
			do
			{
				if(navigator.CurrentTreeNavigator == null)
					continue;

				if(navigator.CurrentTreeNavigator.MoveToMedia(mediaUri))
					if(this.MoveTo(navigator))
						return true;
			}
			while(navigator.MoveToNextTree());
			return false;
		}

		public MediaLibraryNavigator Clone()
		{
			MediaLibraryNavigator clone = new MediaLibraryNavigator(this._libraries, true);
			clone._currentLibrary = this._currentLibrary;
			clone._currentTree = this._currentTree;
			if(this._currentTreeNavigator != null)
				clone._currentTreeNavigator = this._currentTreeNavigator.Clone();
			return clone;
		}

		public IMediaLibrary CurrentLibrary
		{
			get { return _currentLibrary; }
		}

		public IMediaTree CurrentTree
		{
			get { return _currentTree; }
			private set
			{
				if(value == _currentTree)
					return;
				_currentTree = value;
				_currentTreeNavigator = (value != null) ? new MediaTreeNavigator(value) : null;
			}
		}

		public MediaTreeNavigator CurrentTreeNavigator
		{
			get { return _currentTreeNavigator; }
		}

	}
}
